#include "Bezier.hpp"
#include <vector>
#include <cmath>
#include <iostream>
#include <fstream>
#include <utility>

const double PI = acos(-1.); // Define PI

// Define the heart-shaped curve function
class HeartFunction {
public:
    // Function to generate (x, y) points on the heart curve based on parameter t
    std::pair<double, double> operator()(double t) {
        double x = sqrt(3) * cos(t * 2 * PI); // x-coordinate
        double y = (2.0 / 3.0) * sqrt(fabs(x)) + (2.0 / 3.0) * sqrt(3) * sin(t * 2 * PI); // y-coordinate
        return std::make_pair(x, y);
    }

    // Function to calculate tangent vector at point t using central difference method
    std::pair<double, double> tangent(double t, double h = 1e-7) {
        double dx = (operator()(t + h).first - operator()(t - h).first) / (2 * h); // Approximate derivative for x
        double dy = (operator()(t + h).second - operator()(t - h).second) / (2 * h); // Approximate derivative for y
        return std::make_pair(dx, dy);
    }
};

int main() {
    int values_of_m[] = {10, 40, 160}; // Different subdivision values for Bezier curve segments

    const std::string output_dir = "output";

    // Loop over different values of m to generate Bezier curves with varying levels of detail
    for (int m : values_of_m) {
        // Vectors to store points and tangents on the heart curve
        std::vector<std::pair<double, double>> tangent;
        std::vector<std::pair<double, double>> p;

        // Generate points and corresponding tangents on the heart curve
        for (double i = 0; i <= m; ++i) {
            p.push_back(HeartFunction()(i / m));
            tangent.push_back(HeartFunction().tangent(i / m));
            //printf("%lf %lf\n", p.back().first, p.back().second); // Output for verification
        }

        // Generate control points for each Bezier curve segment
        std::vector<Bezier> bezierCurves;
        for (int j = 0; j < m; ++j) {
            // Define control points for Bezier curve segment
            std::pair<double, double> q0 = p[j];
            std::pair<double, double> q1 = std::make_pair(q0.first + (1.0 / 3.0 / m) * tangent[j].first, q0.second + (1.0 / 3.0 / m) * tangent[j].second);
            std::pair<double, double> q2 = {p[j + 1].first - (1.0 / 3.0 / m) * tangent[j + 1].first, p[j + 1].second - (1.0 / 3.0 / m) * tangent[j + 1].second};
            std::pair<double, double> q3 = p[j + 1];

            // Create a new Bezier curve and store it
            bezierCurves.emplace_back(q0, q1, q2, q3);
            printf("%lf %lf\n", q1.first, q1.second); // Output for verification
        }

        // Output filename based on current m value
        std::string filename = "F_" + std::to_string(m) + ".txt";
        std::ofstream outfile(output_dir + "/" + filename);
        if (!outfile.is_open()) {
            std::cerr << "Failed to open " << filename << " for writing." << std::endl;
            return 1;
        }

        // Evaluate each Bezier curve at intervals of t and output points
        for (const auto& curve : bezierCurves) {
            for (double t = 0.0; t <= 1; t += 0.1) {
                std::pair<double, double> point = curve.evaluate(t);
                outfile << point.first << " " << point.second << std::endl;
            }
        }
    }

    return 0;
}
